package com.luxsoft.sw3.gasoc.model;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.annotations.Type;
import org.hibernate.validator.NotNull;

import com.luxsoft.siipap.model.CantidadMonetaria;
import com.luxsoft.siipap.model.Direccion;
import com.luxsoft.siipap.model.Sucursal;
import com.luxsoft.siipap.model.UserLog;
import com.luxsoft.siipap.model.core.Cliente;
import com.luxsoft.siipap.ventas.model.Venta;
import com.luxsoft.siipap.ventas.model.VentaDet;
import com.luxsoft.siipap.ventas.model.Venta.TipoDeVenta;

/**
 * Entidad registrar la facturacion a public en general 
 * 
 * @author Ruben Cancino Ramos
 *
 */
@Entity
@Table(name="SX_FACTURAS"
	,uniqueConstraints=@UniqueConstraint(columnNames={"SERIE","FOLIO"})
	)
public class Factura {
	
	@Id @GeneratedValue (strategy=GenerationType.AUTO)
	@Column (name="FACTURA_ID")
	private Long id;
	
	@Column(name="SERIE",nullable=false)
	private String serie;
	
	@Enumerated(EnumType.STRING)
	@Column(name = "TIPO_VENTA", nullable = false, length = 20)
	private TipoDeVenta tipoDeVenta;
	
	@Column(name="FOLIO",nullable=false)
	private String folio;
	
	@Column(name="TIPO",nullable=false,length=20)
	private String tipo;
	
	@Column(name="FECHA",nullable=false)
	@Type(type="date")
	private Date fecha;
	
	@ManyToOne(optional = false,fetch=FetchType.EAGER)			
	@JoinColumn(name = "CLIENTE_ID", nullable = false)
	@NotNull(message="El cliente es mandatorio")
	private Cliente cliente;
	
	@ManyToOne(optional = false,fetch=FetchType.EAGER)
	@JoinColumn(name = "SUCURSAL_ID", nullable = false)
	private Sucursal sucursal;	
	
	@Column(name = "NOMBRE", nullable = true)
    private String nombre;
	
	@Column(name="RFC",length=13,nullable=false)
	private String rfc;
	
	@Column(name="IVA_DESG",nullable=false)
	private boolean desglosarIva=true;
	
	@Embedded
    private Direccion direccionFiscal=new Direccion();
	
	@OneToMany(mappedBy="factura",fetch=FetchType.LAZY,cascade={
			 CascadeType.PERSIST
				,CascadeType.MERGE
				,CascadeType.REMOVE
			}
	)
    private Set<Venta> ventas=new HashSet<Venta>();
	
	@Column(name = "IMPORTE", nullable = false)
	private BigDecimal importe = BigDecimal.ZERO;
	
	@Column(name = "IMPUESTO", nullable = false)
	private BigDecimal impuesto = BigDecimal.ZERO;
	
	@Column(name = "TOTAL", nullable = false)
	private BigDecimal total = BigDecimal.ZERO;
	
	@Column(name="NO_APROBACION",nullable=false)
	private Integer noAprobacion;
	
	@Column(name="ANO_APROBACION",nullable=false)
	private Integer anoAprobacion; 
	
	@Column(name="ASIGNACION",nullable=false)
	private Date asignacion;
	
	@Column(name="FOLIOS_ASIGNADOS",length=20)
	private String foliosAsignados;
	
	@Embedded
	@AttributeOverrides({
	       @AttributeOverride(name="createUser",	column=@Column(name="CREADO_USR"	,nullable=true,insertable=true,updatable=false)),
	       @AttributeOverride(name="updateUser",	column=@Column(name="MODIFICADO_USR",nullable=true,insertable=true,updatable=true)),
	       @AttributeOverride(name="creado", 		column=@Column(name="CREADO"		,nullable=true,insertable=true,updatable=true)),
	       @AttributeOverride(name="modificado", 	column=@Column(name="MODIFICADO"	,nullable=true,insertable=true,updatable=true))
	   })
	private UserLog log=new UserLog();

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getSerie() {
		return serie;
	}

	public void setSerie(String serie) {
		this.serie = serie;
	}
	

	public TipoDeVenta getTipoDeVenta() {
		return tipoDeVenta;
	}

	public void setTipoDeVenta(TipoDeVenta tipoDeVenta) {
		this.tipoDeVenta = tipoDeVenta;
	}

	public String getFolio() {
		return folio;
	}

	public void setFolio(String folio) {
		this.folio = folio;
	}

	public String getTipo() {
		return tipo;
	}

	public void setTipo(String tipo) {
		this.tipo = tipo;
	}

	public Date getFecha() {
		return fecha;
	}

	public void setFecha(Date fecha) {
		this.fecha = fecha;
	}

	public Cliente getCliente() {
		return cliente;
	}

	public void setCliente(Cliente cliente) {
		this.cliente = cliente;
	}

	public Sucursal getSucursal() {
		return sucursal;
	}

	public void setSucursal(Sucursal sucursal) {
		this.sucursal = sucursal;
	}

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public String getRfc() {
		return rfc;
	}

	public void setRfc(String rfc) {
		this.rfc = rfc;
	}

	public Direccion getDireccionFiscal() {
		return direccionFiscal;
	}

	public void setDireccionFiscal(Direccion direccionFiscal) {
		this.direccionFiscal = direccionFiscal;
	}

	public Set<Venta> getVentas() {
		return ventas;
	}

	public void setVentas(Set<Venta> ventas) {
		this.ventas = ventas;
	}

	public BigDecimal getImporte() {
		return importe;
	}

	public void setImporte(BigDecimal importe) {
		this.importe = importe;
	}

	public BigDecimal getImpuesto() {
		return impuesto;
	}

	public void setImpuesto(BigDecimal impuesto) {
		this.impuesto = impuesto;
	}

	public BigDecimal getTotal() {
		return total;
	}

	public void setTotal(BigDecimal total) {
		this.total = total;
	}
	

	public UserLog getLog() {
		if(log==null){
			log=new UserLog();
		}
		return log;
	}

	public void setLog(UserLog log) {
		this.log = log;
	}
	
	public Integer getNoAprobacion() {
		return noAprobacion;
	}

	public void setNoAprobacion(Integer noAprobacion) {
		this.noAprobacion = noAprobacion;
	}

	public Integer getAnoAprobacion() {
		return anoAprobacion;
	}

	public void setAnoAprobacion(Integer anoAprobacion) {
		this.anoAprobacion = anoAprobacion;
	}
	
	public Date getAsignacion() {
		return asignacion;
	}

	public void setAsignacion(Date asignacion) {
		this.asignacion = asignacion;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((folio == null) ? 0 : folio.hashCode());
		result = prime * result + ((serie == null) ? 0 : serie.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Factura other = (Factura) obj;
		if (folio == null) {
			if (other.folio != null)
				return false;
		} else if (!folio.equals(other.folio))
			return false;
		if (serie == null) {
			if (other.serie != null)
				return false;
		} else if (!serie.equals(other.serie))
			return false;
		return true;
	}

	public String toString(){
		return new ToStringBuilder(this,ToStringStyle.SHORT_PREFIX_STYLE)
		.append("Nombre",getNombre())
		.append("Serie",getSerie())
		.append("Folio",getFolio())
		.append("Fecha",getFecha())
		.toString();
		
	}
	
	public void actualizar(){
		CantidadMonetaria importe=CantidadMonetaria.pesos(0);
		CantidadMonetaria impuesto=CantidadMonetaria.pesos(0);
		CantidadMonetaria total=CantidadMonetaria.pesos(0);
		for(Venta v:ventas){
			importe=importe.add(CantidadMonetaria.pesos(v.getImporte()));
			impuesto=impuesto.add(CantidadMonetaria.pesos(v.getImpuesto()));
			total=total.add(CantidadMonetaria.pesos(v.getTotal()));
		}		
		setImporte(importe.amount());
		setImpuesto(impuesto.amount());
		setTotal(total.amount());
		
	}

	public boolean isDesglosarIva() {
		return desglosarIva;
	}

	public void setDesglosarIva(boolean desglosarIva) {
		this.desglosarIva = desglosarIva;
	}
	
	@Transient
	private List<VentaDet> partidas=new ArrayList<VentaDet>();
	
	public List<VentaDet> getPartidas(){
		if(partidas.isEmpty()){
			for(Venta v:getVentas()){
				partidas.addAll(v.getPartidas());
			}			
		}
		return partidas;
	}
	
	@Transient
	private List<FacturaConcepto> conceptos=new ArrayList<FacturaConcepto>();
	
	public List<FacturaConcepto> getConceptos(){
		if(conceptos.isEmpty()){
			for(VentaDet v:getPartidas()){
				conceptos.add(new FacturaConcepto(v));
			}			
		}
		return conceptos;
	}
	
	/**
	 * NO ES UN IMPORTE BRUTO ES UN IMPORTE TOTAL CON IVA SUMANDO LAS PARTIDAS
	 * @return
	 */
	public BigDecimal getImporteBruto(){
		BigDecimal importe=BigDecimal.ZERO;
		for(Venta v:getVentas()){
			importe=importe.add(v.getImporteBruto());
		}
		return importe;
	}
	
	public BigDecimal getImporteBrutoSinIVA(){
		BigDecimal importe=BigDecimal.ZERO;
		for(Venta v:getVentas()){
			importe=importe.add(v.getImporteBrutoReal());
		}
		return importe;
	}
	
	public BigDecimal getImporteDescuento(){
		BigDecimal importe=BigDecimal.ZERO;
		for(Venta v:getVentas()){
			importe=importe.add(v.getImporteDescuento());
		}
		return importe;
	}
	
	public BigDecimal getImporteDescuentoSinIva(){
		BigDecimal importe=BigDecimal.ZERO;
		for(Venta v:getVentas()){
			importe=importe.add(v.getImporteDescuentoSinIva());
		}
		return importe;
	}
	
	public BigDecimal getSubTotal2(){
		BigDecimal importe=BigDecimal.ZERO;
		for(Venta v:getVentas()){
			importe=importe.add(v.getSubTotal2());
		}
		return importe;
	}
	
	
	public String getFoliosAsignados() {
		return foliosAsignados;
	}

	public void setFoliosAsignados(String foliosAsignados) {
		this.foliosAsignados = foliosAsignados;
	}


	public static class FacturaConcepto{
		
		private double cantidad;
		private String clave;
		private String socio;
		private String descripcion;
		private double precio;
		private double importe;
		private String comentario1;
		private String comentario2;
				
		public FacturaConcepto(VentaDet det){
			this.cantidad=Math.abs(det.getCantidad());
			this.clave=det.getClave();
			this.socio=det.getSocioNombre();
			this.descripcion=det.getDescripcion();
			
			if(det.getVenta().getClave().equals("1")
				|| det.getVenta().getCliente().getRfc()==null
				|| det.getVenta().getCliente().getRfc().equals("XAXX010101000")){
				
				this.importe=det.getImporteConIva().doubleValue();
				this.precio=det.getPrecioConIva().doubleValue();
			}else{
				this.importe=det.getImporte().doubleValue();
				this.precio=det.getPrecio().doubleValue();
			}
			this.comentario1=det.getComentario();
		}

		public FacturaConcepto(double cantidad, String clave,String socio,
				String descripcion, double precio, double importe,
				String comentario1, String comentario2) {
			super();
			this.cantidad = cantidad;
			this.clave = clave;
			this.socio=socio;
			this.descripcion = descripcion;
			this.precio = precio;
			this.importe = importe;
			this.comentario1 = comentario1;
			this.comentario2 = comentario2;
		}

		public double getCantidad() {
			return cantidad;
		}

		public void setCantidad(double cantidad) {
			this.cantidad = cantidad;
		}

		public String getClave() {
			return clave;
		}

		public void setClave(String clave) {
			this.clave = clave;
		}

		public String getDescripcion() {
			return descripcion;
		}

		public void setDescripcion(String descripcion) {
			this.descripcion = descripcion;
		}

		public double getPrecio() {
			return precio;
		}

		public void setPrecio(double precio) {
			this.precio = precio;
		}

		public double getImporte() {
			return importe;
		}

		public void setImporte(double importe) {
			this.importe = importe;
		}

		public String getComentario1() {
			return comentario1;
		}

		public void setComentario1(String comentario1) {
			this.comentario1 = comentario1;
		}

		public String getComentario2() {
			return comentario2;
		}

		public void setComentario2(String comentario2) {
			this.comentario2 = comentario2;
		}

		public String getSocio() {
			return socio;
		}

		public void setSocio(String socio) {
			this.socio = socio;
		}
		
		
		
		
	}
	
}
